﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Windows.Controls.Primitives;
using Microsoft.Phone.Shell;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace Darku.PatternLock
{
    public class PatternLockDialog : ContentControl
    {
        //Layout pannels for the control display
        Grid popupGrid;
        Grid layout;
        Grid layoutBackground;
        Grid patternContent;
        StackPanel infoPanel;

        //Max number of tries before the MaximumTries event is triggered. Default would be three(3)
        int maxTries;

        // An array of all the inner borders
        Border[] allInnerBorders;

        // An array of all the outter borders
        Border[] allOuterBorders;

        // The textblock that displays the App's title
        TextBlock tbAppTitle;

        // The textblock that displays any info on the Lock Dialog
        TextBlock tbInfo;

        // The textblock that displays status on the Lock Dialog
        TextBlock tbStatus;


        int matrix;
        int borderSize;

        // boolean values to make some checks
        bool check = true;
        bool finish = true;
        bool comparePasswords = false;

        // the property that store the chosen pattern
        string password = "";

        // properties used to check chosen patterns for consistency/mismatch
        string passwordCompare1 = "";
        string passwordCompare2 = "";

        string givenPassword = "";
        string previous = "";

        // The timer to check the pattern after chosen
        DispatcherTimer timer;

        // The close event of the Lock Dialog
        public event EventHandler Closed;


        // The MaximumTries event of the Lock Dialog
        public event EventHandler MaximumTries;

        // The various border colors of the matrix. Fully customisable
        SolidColorBrush outerBorderBackground, outerBorderBrush, outerBorderBackgroundClick, outerBorderBrushClick,
            innerBorderBackground, innerBorderBrush, innerBorderBackgroundClick, innerBorderBrushClick;

        // Properties used to set the background image
        ImageBrush imageBrush ;
        Image image ;


        /// <summary>
        /// Sets the background image of the Lock Dialog
        /// </summary>
        public BitmapImage BackgroundImage
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the chosen pattern from the Lock Dialog
        /// </summary>
        public string Password
        {
            get { return password; }
            //set { password=value; }
        }

        ///// <summary>
        ///// Gets and sets the number of maximum tries 
        ///// </summary>
        public int MaxTries
        {
            get { return maxTries; }
            set { maxTries = value; }
        }


        ///// <summary>
        ///// Gets and sets the given password 
        ///// </summary>
        public string GivenPassword
        {
            get { return givenPassword; }
            set { givenPassword = value; }
        }

        /// <summary>
        /// Sets the border color of the matrix
        /// </summary>
        public SolidColorBrush OuterBorderBackground
        {
            get { return outerBorderBackground; }
            set {
                if(value!=null)
                {
                    outerBorderBackground=value;
                }
            }
        }

        /// <summary>
        /// Sets the border color of the matrix
        /// </summary>
        public SolidColorBrush OuterBorderBackgroundClicked
        {

            get { return outerBorderBackgroundClick; }
            set
            {
                if (value != null)
                {
                    outerBorderBackgroundClick = value;
                }
            }
        }

        /// <summary>
        /// Sets the border color of the matrix
        /// </summary>
        public SolidColorBrush OuterBorderBrush
        {
            get { return outerBorderBrush; }
            set
            {
                if (value != null)
                {
                    outerBorderBrush = value;
                }
            }
        }

        /// <summary>
        /// Sets the border color of the matrix
        /// </summary>
        public SolidColorBrush OuterBorderBrushClicked
        {
            get { return outerBorderBrushClick; }
            set
            {
                if (value != null)
                {
                    outerBorderBrushClick = value;
                }
            }
        }

        /// <summary>
        /// Sets the border color of the matrix
        /// </summary>
        public SolidColorBrush InnerBorderBackground
        {
            get { return innerBorderBackground; }
            set
            {
                if (value != null)
                {
                    innerBorderBackground = value;
                }
            }
        }

        /// <summary>
        /// Sets the border color of the matrix
        /// </summary>
        public SolidColorBrush InnerBorderBackgroundClicked
        {
            get { return innerBorderBackgroundClick; }
            set
            {
                if (value != null)
                {
                    innerBorderBackgroundClick = value;
                }
            }
        }

        /// <summary>
        /// Sets the border color of the matrix
        /// </summary>
        public SolidColorBrush InnerBorderBrush
        {
            get { return innerBorderBrush; }
            set
            {
                if (value != null)
                {
                    innerBorderBrush = value;
                }
            }
        }

        /// <summary>
        /// Sets the border color of the matrix
        /// </summary>
        public SolidColorBrush InnerBorderBrushClicked
        {
            get { return innerBorderBrushClick; }
            set
            {
                if (value != null)
                {
                    innerBorderBrushClick = value;
                }
            }
        }

        /// <summary>
        /// Gests and sets the the app's title to be displayed on the Lock Dialog
        /// </summary>
        public string AppTitle
        {
            get;
            set;
        }

        

        /// <summary>
        /// Constructor of the Lock Dialog
        /// </summary>
        /// <param name="_appTitle">Sets the App title to be displayed on the Lock Dialog</param>
        /// <param name="_matrix">Enum - sets the matrix size of the Lock Dialog</param>
        public PatternLockDialog(string _appTitle, PatternLockMatrix _matrix)
        {
            this.DefaultStyleKey = typeof(PatternLockDialog);

            AppTitle = _appTitle;
            maxTries = 3;

            //Initialise some properties
            Initialise(_matrix);
        }


        /// <summary>
        /// Constructor of the Lock Dialog
        /// </summary>
        /// <param name="_appTitle">Sets the App title to be displayed on the Lock Dialog</param>
        /// <param name="_matrix">Enum - sets the matrix size of the Lock Dialog</param>
        /// <param name="_maxTries">Sets the maximum number of tries</param>
        public PatternLockDialog(string _appTitle,int _maxTries, PatternLockMatrix _matrix)
        {
            this.DefaultStyleKey = typeof(PatternLockDialog);

            AppTitle = _appTitle;
            MaxTries = _maxTries;

            //Initialise some properties
            Initialise(_matrix);
        }

        private void Initialise(PatternLockMatrix _matrix)
        {
            if (_matrix == PatternLockMatrix.Three)
            {
                matrix = 3;
                borderSize = 100;
            }
            else if (_matrix == PatternLockMatrix.Four)
            {
                matrix = 4;
                borderSize = 90;
            }
            else
            {
                matrix = 3;
                borderSize = 100;
            }

            // setting the default colors of the borders used in the matrix
            InnerBorderBackground = new SolidColorBrush(Colors.White);
            InnerBorderBrush = new SolidColorBrush(Colors.Transparent);
            InnerBorderBackgroundClicked = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
            InnerBorderBrushClicked = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));

            OuterBorderBackground = new SolidColorBrush(Colors.Transparent);
            OuterBorderBrush = new SolidColorBrush(Colors.Transparent);
            OuterBorderBackgroundClicked = new SolidColorBrush(Color.FromArgb(80, 255, 255, 255));
            OuterBorderBrushClicked = new SolidColorBrush(Color.FromArgb(255, 82, 255, 47));


            // initialising the timer
            timer = new DispatcherTimer();
            timer.Tick += new EventHandler(Timer_Tick);
            timer.Interval = new TimeSpan(0, 0, 1);
            timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            if (!check)
            {
                //Checks if its in PasswordCompare Mode
                if (comparePasswords)
                {
                    if (password == givenPassword)
                    {
                        Close();
                    }
                    else
                    {
                        tbStatus.Text = "Pattern mismatch. Please try again";
                        maxTries--;

                        //If the maximum number of trials has been exhausted , trigger the MaximumTries event
                        if (maxTries <= 0 )
                        {
                            maxTries = 3;

                            if ( this.MaximumTries != null)
                            {
                                this.MaximumTries(this, EventArgs.Empty);
                            }
                        }
                    }


                }
                else
                {
                    if (password.Length < 10)
                    {
                        tbStatus.Text = "Connect at least 5 dots";
                    }
                    else
                    {

                        if (passwordCompare1 == "")
                        {
                            passwordCompare1 = password;
                            password = "";
                            tbInfo.Text = "Choose pattern again";
                        }
                        else
                        {
                            passwordCompare2 = password;
                            password = "";
                        }

                        if (passwordCompare1 != "" && passwordCompare2 != "")
                        {
                            if (passwordCompare1 == passwordCompare2)
                            {
                                password = passwordCompare2;
                                passwordCompare1 = "";
                                passwordCompare1 = "";
                                Close();
                            }
                            else
                            {
                                passwordCompare1 = "";
                                passwordCompare2 = "";
                                password = "";
                                tbInfo.Text = "Choose pattern";
                                tbStatus.Text = "Pattern mismatch. Please try again";
                            }
                        }
                    }
                    
                }


                timer.Stop();
                reset();
                finish = true;

            }

        }

        #region overrides

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

        }

        #endregion

        internal Popup popup
        {
            get;
            private set;
        }

        private static PhoneApplicationFrame RootVisual
        {
            get
            {
                return Application.Current == null ? null : Application.Current.RootVisual as PhoneApplicationFrame;
            }
        }

        /// <summary>
        /// Shows the Lock Dialog for the user to choose a pattern
        /// </summary>
        public void Show()
        {
            var page = ((PhoneApplicationPage)RootVisual.Content);
            
            comparePasswords = false;
            if (this.popup == null)
            {
                // intialise the popup if null
                this.popup = new Popup();
            }

            if (RootVisual != null)
            {

                if (page.ApplicationBar != null && page.ApplicationBar.IsVisible)
                {
                    page.ApplicationBar.IsVisible = false;
                }
                
                // Hook up into the back key press event of the current page
                page.BackKeyPress += new EventHandler<System.ComponentModel.CancelEventArgs>(PatternLockDialog_BackKeyPress);

            }


            if (this.popup != null && Application.Current.RootVisual != null)
            {
                SystemTray.IsVisible = false;

                // Show popup
                popup = new Popup();
                popupGrid = Layout();

                tbInfo.Text = "Choose pattern";
                popupGrid.Height = page.ActualHeight + 100;
                popupGrid.Width = page.ActualWidth;

                popup.Child = popupGrid;
                popup.IsOpen = true;
            }

        }

        /// <summary>
        /// Shows the Lock Dialog to compare a chosen pattern to a given pattern
        /// </summary>
        /// <param name="_givenPassword">The pattern to compare with</param>
        public void Show(string _givenPassword)
        {
            GivenPassword = _givenPassword;
            comparePasswords = true;
            var page = ((PhoneApplicationPage)RootVisual.Content);

            if (this.popup == null)
            {
                // intialise the popup if null
                this.popup = new Popup();

            }


            if (RootVisual != null)
            {

                if (page.ApplicationBar != null && page.ApplicationBar.IsVisible)
                {
                    page.ApplicationBar.IsVisible = false;
                }

                // Hook up into the back key press event of the current page
                page.BackKeyPress += new EventHandler<System.ComponentModel.CancelEventArgs>(PatternLockDialog_BackKeyPress);

            }


            if (this.popup != null && Application.Current.RootVisual != null)
            {
                SystemTray.IsVisible = false;

                // Show popup
                popup = new Popup();
                popupGrid = Layout();

                tbInfo.Text = "Draw pattern to unlock";
                popupGrid.Height = page.ActualHeight + 50;
                popupGrid.Width = page.ActualWidth;

              
                popup.Child = popupGrid;
                
                popup.IsOpen = true;
            }

        }


        void PatternLockDialog_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // Cancel navigation
            e.Cancel = true;

            // If not in compare mode then close the Lock Dialog
            if (!comparePasswords)
            { Close(); }
        }

        public void Close()
        {
            // Unhook the BackKeyPress event handler
            ((PhoneApplicationPage)RootVisual.Content).BackKeyPress -= new EventHandler<System.ComponentModel.CancelEventArgs>(PatternLockDialog_BackKeyPress);
            
            // close popup
            popup.IsOpen = false;

            if (((PhoneApplicationPage)RootVisual.Content).ApplicationBar != null && !((PhoneApplicationPage)RootVisual.Content).ApplicationBar.IsVisible)
            ((PhoneApplicationPage)RootVisual.Content).ApplicationBar.IsVisible = true;
            SystemTray.IsVisible = true;

            // Raise closed event 
            if (this.Closed != null)
            {
                this.Closed(this, EventArgs.Empty);
            }
        }



        private Grid Layout()
        {

            layout = new Grid();

            layoutBackground= new Grid();
            layoutBackground.Background = new SolidColorBrush(Colors.Gray);

            RowDefinition row1 = new RowDefinition();
            row1.Height =new System.Windows.GridLength(250);

            RowDefinition row2 = new RowDefinition();

            layout.RowDefinitions.Add(row1);
            layout.RowDefinitions.Add(row2);
            layout.Background = new SolidColorBrush(Colors.Gray);


            patternContent = new Grid();

            allInnerBorders = new Border[matrix * matrix];
            allOuterBorders = new Border[matrix * matrix];

            for (int b = 0; b < matrix; b++)
            {
                patternContent.ColumnDefinitions.Add(new ColumnDefinition());
                patternContent.RowDefinitions.Add(new RowDefinition());
            }

            int start = 0;
            for (int row = 0; row < matrix; row++)
            {
                for (int column = 0; column < matrix; column++)
                {
                    allOuterBorders[start] = new Border();
                    allOuterBorders[start].CornerRadius = new CornerRadius(borderSize/2);
                    allOuterBorders[start].Height = borderSize;
                    allOuterBorders[start].Width = borderSize;
                    allOuterBorders[start].Tag = start + 1;
                    allOuterBorders[start].SetValue(Grid.RowProperty, row);
                    allOuterBorders[start].SetValue(Grid.ColumnProperty, column);
                    allOuterBorders[start].Background = OuterBorderBackground;
                    allOuterBorders[start].BorderThickness = new Thickness(4);
                    allOuterBorders[start].BorderBrush = OuterBorderBrush;
                    allOuterBorders[start].MouseEnter += new MouseEventHandler(border_MouseEnter);

                    allInnerBorders[start] = new Border();
                    allInnerBorders[start].CornerRadius = new CornerRadius(15);
                    allInnerBorders[start].Height = 30;
                    allInnerBorders[start].Width = 30;
                    allInnerBorders[start].Tag = start + 1;
                    allInnerBorders[start].Background = InnerBorderBackground;
                    allInnerBorders[start].BorderThickness = new Thickness(2);
                    allInnerBorders[start].BorderBrush = InnerBorderBrush;
                    allInnerBorders[start].Margin = new Thickness(10);
                    

                    allOuterBorders[start].Child = allInnerBorders[start];

                    patternContent.Children.Add(allOuterBorders[start]);
                    
                    start = start + 1;
                }
            }

            infoPanel = new StackPanel();
            infoPanel.Orientation= System.Windows.Controls.Orientation.Vertical;
            infoPanel.SetValue(Grid.RowProperty,0);
            infoPanel.Background = new SolidColorBrush(Color.FromArgb(90,00,00,00));

            tbAppTitle=new TextBlock();
            tbAppTitle.Text = AppTitle;
            tbAppTitle.FontSize = 20;
            tbAppTitle.Margin = new Thickness(5,10,5,20);
            tbAppTitle.Foreground = new SolidColorBrush(Colors.White);

            tbInfo = new TextBlock();
            tbInfo.Text = "";
            tbInfo.FontSize = 40;
            tbInfo.Margin = new Thickness(15);
            tbInfo.Foreground = new SolidColorBrush(Colors.White);


            tbStatus = new TextBlock();
            tbStatus.Text = "";
            tbStatus.FontSize = 30;
            tbStatus.HorizontalAlignment = HorizontalAlignment.Center;
            tbStatus.FontWeight = FontWeights.Bold;
            tbStatus.Margin = new Thickness(15);
            tbStatus.TextWrapping = TextWrapping.Wrap;
            tbStatus.Foreground = new SolidColorBrush(Colors.Red);

            infoPanel.Children.Add(tbAppTitle);
            infoPanel.Children.Add(tbInfo);
            infoPanel.Children.Add(tbStatus);

            patternContent.SetValue(Grid.RowProperty, 1);
            patternContent.Background = new SolidColorBrush(Colors.Transparent);
            patternContent.Height = 480;
            patternContent.ShowGridLines = false;
            patternContent.VerticalAlignment = System.Windows.VerticalAlignment.Top;


            if ( BackgroundImage!=null)
            {
                image = new Image();
                imageBrush = new ImageBrush();
                image.Source = BackgroundImage;
                imageBrush.ImageSource = image.Source;
                imageBrush.Stretch = Stretch.UniformToFill;
                imageBrush.Opacity = 0.7;
                layout.Background = imageBrush;

            }
            else
            {
                layout.Background = new SolidColorBrush(Colors.Gray);
            }


            layout.Children.Add(infoPanel);
            layout.Children.Add(patternContent);

            layoutBackground.Children.Add(layout);
            layoutBackground.Height = 1000;

            return layoutBackground;

        }

        void reset()
        {
            int start = 0;
            for (int row = 0; row < matrix; row++)
            {
                for (int column = 0; column < matrix; column++)
                {
                    allOuterBorders[start].CornerRadius = new CornerRadius(borderSize/2);
                    allOuterBorders[start].Height = borderSize;
                    allOuterBorders[start].Width = borderSize;
                    allOuterBorders[start].Tag = start + 1;
                    allOuterBorders[start].SetValue(Grid.RowProperty, row);
                    allOuterBorders[start].SetValue(Grid.ColumnProperty, column);
                    allOuterBorders[start].Background = OuterBorderBackground;
                    allOuterBorders[start].BorderThickness = new Thickness(4);
                    allOuterBorders[start].BorderBrush = OuterBorderBrush;
                    allOuterBorders[start].MouseEnter += new MouseEventHandler(border_MouseEnter);

                    allInnerBorders[start].CornerRadius = new CornerRadius(15);
                    allInnerBorders[start].Height = 30;
                    allInnerBorders[start].Width = 30;
                    allInnerBorders[start].Tag = start + 1;
                    allInnerBorders[start].Background = InnerBorderBackground;
                    allInnerBorders[start].BorderThickness = new Thickness(2);
                    allInnerBorders[start].BorderBrush = InnerBorderBrush;
                    allInnerBorders[start].Margin = new Thickness(10);

                    start = start + 1;
                }
            }

            password = "";
            previous = "";
            finish = false;

        }

        private void border_MouseEnter(object sender, MouseEventArgs e)
        {
            tbStatus.Text = "";
            if (finish)
            {
                Border outerBorder = (Border)sender;

                check = true;

                if (previous != outerBorder.Tag.ToString())
                {
                    // if the border has not already been chosen
                    if (!password.Contains(outerBorder.Tag.ToString()+"-"))
                    {
                        // store the immediate chosen border
                        previous = outerBorder.Tag.ToString();
                        password += previous+"-";
                    }
                }

                // mark the matrix as chosen
                allInnerBorders[int.Parse(outerBorder.Tag.ToString()) - 1].Background = InnerBorderBackgroundClicked;
                allInnerBorders[int.Parse(outerBorder.Tag.ToString()) - 1].BorderBrush = InnerBorderBrushClicked;

                outerBorder.Background = OuterBorderBackgroundClicked;
                outerBorder.BorderBrush = OuterBorderBrushClicked;

                // set boolean values
                finish = true;
                check = false;

                //run a one second timer to check if finished
                timer.Start();
            }
            else
            { finish = true; }

        }



    }

    /// <summary>
    /// The enum to choose a 3X3 or 4X4 matrix
    /// </summary>
    public enum PatternLockMatrix
    {
        Three,
        Four
    }


}
